﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using WCFService.Labb1;
using System.Net;
using System.IO;
using System.Xml;

namespace WCFService
{
    public class Service1 : IService1
    {
        private CityDataTable cityDataTable = new CityDataTable();
        private List<Calculation> calculations = new List<Calculation>();

        //Data members.
        //This tracks the devices-to-channelURI list, which gets updated by calls to
        //SubscribeMyPhone();
        private static Dictionary<Guid, Uri> SubscriptionURIs = new Dictionary<Guid, Uri>();

        public Service1()
        {
            //TODO: Load from database into SubscriptionURIs here.
        }

        public bool Login(string user, string password)
        {
            if (user.Equals("a") && password.Equals("qwerty"))
            {
                return true;
            }
            return false;
        }

        public List<City> GetCities()
        {
            return cityDataTable.GetCities();
        }

        /**
         * Return -1 if not possible
         *  Otherwise return the new ID.
         */
        public Calculation NewCalculation(Calculation calculation)
        {
            List<City> citiesToVisit = calculation.Cities;
            
            // Compute
            TSP tsp = new TSP();
            calculation.Result = tsp.runTSP(calculation.Cities);
            calculation.Distance = tsp.distance;

            PushToast("iRemoteComputing", "Calculation finished...");
            //PushTileUpdate("Ny ongoing", 1, "http://dreamgreat.com/assets/assets_banner/red%20star%20no%20background%20no%20text.png");
            PushTileUpdate("Calculation finished", 1, null);

            return calculation;
        }

        //This overwrites/creates entries for each phone that wants to receive Push Notifications.
        //Note: This list is reset whenever application restarts -- in real life, you will want
        //to store this data from the SubscriptionURIs list into a database so it persists. Hence,
        //there are TODO sections in this code.
        public void SubscribeMyPhone(Guid phoneID, string channelURI)
        {
            Uri thisURI = new Uri(channelURI);
            if (SubscriptionURIs.ContainsKey(phoneID))
            {
                //update the existing URI entry for this phone, if need be
                SubscriptionURIs[phoneID] = thisURI;
            }
            else
            {
                //otherwise, add a subscription for this phone
                SubscriptionURIs.Add(phoneID, thisURI);
            }
            // TODO: Save this data in a database, otherwise it will be lost on application restart.
        }
        //Sends the received text value as a Raw notification -- which can only be received
        //while the subscribed phone app is running. Raw notifications have a size limit of 1K.
        public void PushRawData(string rawMessage)
        {
            //Encode the message as a byte[] array as the Notification Service Expects.
            System.Text.UTF8Encoding encoding = new UTF8Encoding();
            //Send this message to all subscribed devices.
            sendPushToSubscribedURIs(encoding.GetBytes(rawMessage), "raw");
        }

        //Sends a Toast notification, using the arguments to define the notification's Title and Message.
        public void PushToast(string ToastTitle, string ToastMessage)
        {
            //Use XMLWriter to construct notification structure/contents.
            MemoryStream myStream = new MemoryStream();
            XmlWriter myWriter = XmlWriter.Create(myStream);
            myWriter.WriteStartDocument();
            myWriter.WriteStartElement("wp", "Notification", "WPNotification");
            myWriter.WriteStartElement("wp", "Toast", "WPNotification");
            myWriter.WriteStartElement("wp", "Text1", "WPNotification");
            myWriter.WriteValue(ToastTitle);
            myWriter.WriteEndElement();
            myWriter.WriteStartElement("wp", "Text2", "WPNotification");
            myWriter.WriteValue(ToastMessage);
            myWriter.WriteEndElement();
            myWriter.WriteEndElement();
            myWriter.WriteEndDocument();
            //Transfer Xml Outpute from myWriter's buffer to myStream.
            myWriter.Flush();
            //Send this message to all subscribed devices.
            sendPushToSubscribedURIs(myStream.ToArray(), "toast");
        }

        //Sends the specified Tile Title, Tile Count, and Tile Image URL as a Tile
        //notification, which can only be received while the subscribed phone app is
        //NOT running. The maximum allowed size of the Tile image is 80kb, with a
        //maximum download time of 15 seconds. Only Tile image URLs from the domains listed in
        //Push Client's ListOfAllowedDomains will be accepted. The Tile Title will overwrite
        //the text that labels the tile on the phone's menu, and the Tile Count is
        //shown as a numeric value displayed on top of the Tile image, visually.
        public void PushTileUpdate(string TileTitle, int TileCount, string TileImageURL)
        {
            // Use XmlWriter to construct notification structure/contents.
            MemoryStream myStream = new MemoryStream();
            XmlWriter myWriter = XmlWriter.Create(myStream);
            myWriter.WriteStartDocument();
            myWriter.WriteStartElement("wp", "Notification", "WPNotification");
            myWriter.WriteStartElement("wp", "Tile", "WPNotification");
            myWriter.WriteStartElement("wp", "BackgroundImage", "WPNotification");
            myWriter.WriteValue(TileImageURL);
            myWriter.WriteEndElement();
            myWriter.WriteStartElement("wp", "Count", "WPNotification");
            myWriter.WriteValue(TileCount);
            myWriter.WriteEndElement();
            myWriter.WriteStartElement("wp", "Title", "WPNotification");
            myWriter.WriteValue(TileTitle);
            myWriter.WriteEndElement();
            myWriter.WriteEndElement();
            myWriter.WriteEndDocument();
            //Transfer Xml output from myWriter's buffer to myStream.
            myWriter.Flush();
            //Send this message to all subscribed devices.
            sendPushToSubscribedURIs(myStream.ToArray(), "tile");
        }

        //This enum gives symbolic names to the numeric values used to "batch together"
        //push notifications according to the specified time intervals. You can send them
        //immediately, within 450 seconds, or within 900 seconds. Allowing the Notification
        //Service to batch notifications will let the service group messages together with
        //notifications from other apps to save phone battery life.
        public enum BatchingIntervalValues
        {
            ImmediateTile = 1,
            ImmediateToast = 2,
            ImmediateRaw = 3,
            Wait450SecondsTile = 11,
            Wait450SecondsToast = 12,
            Wait450SecondsRaw = 13,
            Wait900SecondsTile = 21,
            Wait900SecondsToast = 22,
            Wait900SecondsRaw = 23
        }

        //Iterates through SubscriptionURIs, sending the constructed Push Notification to
        //of the notification, which is every subscribed device via the Microsoft
        //Push Notification Service. The payload passed in, is packaged into an HTTP POST
        //message that defines headers containing the notification type and batching interval.
        private static void sendPushToSubscribedURIs(byte[] pushPayload, string notificationType)
        {
            //Iterate through SubscriptionURIs
            foreach (var thisURI in SubscriptionURIs.Values)
            {
            //Add headers to HTTP Post message.
            var myRequest = (HttpWebRequest)WebRequest.Create(thisURI); // Push Client's channelURI
            myRequest.Method = WebRequestMethods.Http.Post;
            myRequest.ContentType = "text/xml";
            myRequest.ContentLength = pushPayload.Length;
        
            // gives this message a unique ID
            myRequest.Headers.Add("X-MessageID", Guid.NewGuid().ToString());
            //Customize or exclude the X-WindowsPhone-Target header based on the notification type.
            switch (notificationType)
            {
            case "toast":
            myRequest.Headers["X-WindowsPhone-Target"] = "toast";
            myRequest.Headers.Add("X-NotificationClass",
            ((int)BatchingIntervalValues.ImmediateToast).ToString());
            break;
            case "tile":
            myRequest.Headers["X-WindowsPhone-Target"] = "token";
            myRequest.Headers.Add("X-NotificationClass",
            ((int)BatchingIntervalValues.ImmediateTile).ToString());
            break;
            case "raw":
            myRequest.Headers.Add("X-NotificationClass",
            ((int)BatchingIntervalValues.ImmediateRaw).ToString());
            //Raw notifications do not specify a value for the X-WindowsPhone-Target header.
            break;
            }
            //Merge headers with payload.
            using (var requestStream = myRequest.GetRequestStream())
            {
            requestStream.Write(pushPayload, 0, pushPayload.Length);
            }
            //Send notification to this phone!
            var response = (HttpWebResponse)myRequest.GetResponse();
            }
        }
    }
}